#!/bin/bash

# 定义选项字典变量
declare -A opt
# 定义脚本短选项
short_opt="u:p:P:h:tn:v:c:d:w:s:"
# 定义脚本长选项
long_opt="user:,port:,host:,password:,table,name:,version:,config:,datadir:,workdir:,slave:"
# 设置脚本选项默认值
opt=([u]=root [p]=1234 [P]=3306 [h]='127.0.0.1' [t]=0 [n]=mysql)
opt+=([w]="$PWD" [d]="$PWD/data" [c]="$PWD/my.cnf" [v]="5.7")
# 定义脚本剩余参数保存变量
args=""
image_name=docker.cat/mysql

err_exit() {
    echo $1
    exit $2
}

run_srv() {
    test -f ${opt[c]} || gen_config $@
    docker run -d --name ${opt[n]}               \
            -e LANG=C.UTF-8                      \
            -e MYSQL_ROOT_PASSWORD=${opt[p]}     \
            -v ${opt[c]}:/etc/mysql/my.cnf       \
            -v ${opt[d]}:/var/lib/mysql          \
            --restart=always                     \
            -p ${opt[P]}:3306                    \
            $image_name:${opt[v]}
}

gen_config() {
    test -e ${opt[c]%/*} || mkdir -p ${opt[c]%/*}
    cat > ${opt[c]} << EOF
[mysqld]
pid-file        = /var/run/mysqld/mysqld.pid
socket          = /var/run/mysqld/mysqld.sock
datadir         = /var/lib/mysql
secure-file-priv= NULL
symbolic-links=0
EOF

    if test "x$1" = 'xmaster'; then
        cat >> ${opt[c]} << EOF
server-id		= ${2-1}
log_bin			= /var/lib/mysql/mysql-bin.log
expire_logs_days	= 10
max_binlog_size   = 100M
EOF
    elif test "x$1" = 'xslave'; then
        cat >> ${opt[c]} << EOF
server-id		= ${2-2}
log_bin			= /var/lib/mysql/mysql-bin.log
expire_logs_days	= 10
max_binlog_size   = 100M
replicate_ignore_db	= mysql,information_schema,performance_schema,sys
EOF
    fi

    echo '!includedir /etc/mysql/conf.d/' >> ${opt[c]}
}

get_script_opt() {
    local cmd=""
    for k in ${!opt[*]} ; do
        test "x${opt[$k]}" = "x0" || cmd+=" -$k ${opt[$k]} "
    done
    echo $cmd
}

run_master() {
    test "x${opt[n]}" = "xmysql" && opt[n]="mysql-master"

    case "$1" in
        grant)
            # run master grant user password [host]
            local user="$2" password="$3" host="${4-%}"
            test -z "$user" -o -z "$host" -o -z "$password" \
                && err_exit "请指定授权的用户 授权密码及主机名..."

            local sql="create user if not exists '$user'@'$host' identified by '$password';"
            sql+="grant replication slave on *.* to '$user'@'$host';"
            sql+="flush privileges;"

            run_client exec <<< "$sql"
            ;;
        create)
            shift
            run_srv master $@
            ;;
        *)
            opt[t]=1
            run_client exec <<< "show master status"
            ;;
    esac

}

run_slave() {
    test "x${opt[n]}" = "xmysql" && opt[n]="mysql-slave"
    case "$1" in
        start)
            run_client exec <<< 'start slave'
            ;;
        stop)
            run_client exec <<< 'stop slave'
            ;;
        connect)
            test "x${opt[s]}" = "x" && err_exit "请使用-s指定主服务器的连接信息..."
            local conn=(${opt[s]//:/ })
            test ${#conn[*]} -lt 6 && err_exit "-s指定参数错误..."
            local sql="stop slave;"
            sql+="change master to master_user='${conn[0]}',"
            sql+="master_password='${conn[1]}',master_host='${conn[2]}',"
            sql+="master_port=${conn[3]},master_log_file='${conn[4]}',"
            sql+="master_log_pos=${conn[5]}; start slave;"
            opt[P]=3306
            run_client exec <<< "$sql"
            ;;
        create)
            # run slave server_id -s user:passwd:host:port:file:pos
            shift
            run_srv slave $@ && sleep 1 && run_slave connect
            ;;
        *)
            run_client exec <<< 'show slave status\\G'
            ;;
    esac

}

container_exists() {
# 判断容器名称是否存在
    docker ps -a --format "table {{.Names}}" | egrep -q "^$1$"
}

get_container_name() {
# 返回防止重复的容器名称
    local name="${opt[n]}"
    let i=0
    while container_exists "$name" ; do
        name="${name}_$i"
        let i++
    done
    echo $name
}

get_mysql_opt() {
    local t="-t"
    test "x${opt[t]}" = "x0" && t=""
    echo "mysql -h${opt[h]} -P${opt[P]} -u${opt[u]} -p${opt[p]} --default-character-set=utf8 $RDS_OPTS $t"
}

get_docker_cmd_opt() {
    local cmd="docker $1 -i$2 "
    test "x$1" = "xrun"                                               \
        && cmd+="--rm -e LANG=C.UTF-8 -v ${opt[w]}:/works -w /works  $image_name:${opt[v]}" \
        || cmd+=" ${opt[n]}"
    echo $cmd
}

get_stdin() {
    read -t 0.2
    echo "$REPLY"
}

# 在已有运行服务器的情况下, 运行客户端
run_client() {
    local docker_cmd=$1
    local in="$(get_stdin)"
    test "x${opt[h]}" = "x127.0.0.1" && docker_cmd=exec
    container_exists ${opt[n]} || docker_cmd=run
    echo -e "正在运行\033[32;1m ${opt[n]} \033[0m容器..."

    shift

    if test "x$1" = "x" -a "x$in" = "x"; then
        # 没有参数也没有标准输入直接启动mysql客户端交互模式 ./rds
        $(get_docker_cmd_opt $docker_cmd t) $(get_mysql_opt)
    elif test "x$1" = "xbash" ; then
        # 第一个参数为bash, 进入容器bash ./rds bash
        $(get_docker_cmd_opt $docker_cmd t) bash
    elif test "x$in" != "x"; then
        # 如果有标准输入, 则不管是否有参数都执行
        # ./rds <<< "show databases"  ./rds naonao <<< "show tables"
        $(get_docker_cmd_opt $docker_cmd) $(get_mysql_opt) $@ <<< "$in"
    elif test -f "$1"; then
        # 执行sql文件 ./rds slave.sql
        test "x$docker_cmd" = "xexec" && err_exit 'exec不支持导入SQL文件!'
        local file=$(realpath "$1")
        opt[w]=${file%/*}
        file=${file##*/}
        $(get_docker_cmd_opt run) $(get_mysql_opt) <<< "source $file"
    else
        # 参数不为空, 标准输入为空, 运行客户端并且传参 ./rds naonao
        $(get_docker_cmd_opt $docker_cmd t) $(get_mysql_opt) $@
    fi
}

help_show() {
    cat << EOF
命令:
    srv     运行mysql服务器
        rds srv
        rds srv -p123456
        rds srv -P3309 -p123456
    master  运行mysql主服务器
        rds master create -d data-master -c master.cnf -P 3308 -p12345
        rds master status
        rds master grant user password [host]
    slave   运行mysql从服务器
        rds slave create -d slave-master -c slave.cnf -P 3309
        rds slave status
        rds slave stop
        rds slave start
        rds slave connect -s "kyo:1234:3.3.3.109:3308:mysql-bin.000003:865"
    exec    以mysql服务器容器运行mysql客户端
    restart 重启容器
    help    获取本脚本帮助
    以上命令不匹配则以新容器运行mysql客户端
        # rds
        # rds bash
        # rds naonao
        # rds slave.sql
        # rds <<< "show databases"
        # rds naonao <<< "show tables"
        # rds -h 3.3.3.200 -p 3307
        # rds exec
        # rds exec -h 3.3.3.200

选项:
    -u/--user [root]
        指定连接数据库的用户名, 默认值为root
    -p/--password [1234]
        运行服务器则代表root默认密码, 默认值为1234
        运行客户端则代表连接数据库的用户密码, 默认值为1234
    -P/--port [3306]
        运行服务器则代表容器映射端口, 默认值为3306
        运行客户端则代表连接远程数据库的端口, 默认值为3306
    -h/--host [127.0.0.1]
        运行客户端则代表连接远程数据库的地址, 默认值为127.0.0.1
    -t/--table [false]
        运行客户端则代表连接远程数据库的输出格式为表格, 默认值为纯文本
    -w/--workdir [\$PWD]
        运行客户端容器时映射的工作目录, 默认值为当前工作目录
    -c/--config [\$PWD/my.cnf]
        运行服务器容器时绑定的配置文件, 默认值为当前目录下的my.cnf
    -d/--datadir [\$PWD/data]
        运行服务器容器时绑定的数据目录, 默认值为当前目录下的data目录
    -v/--version [8.0]
        指定运行服务器的版本号, 默认为8.0
    -n/--name [mysql]
        指定运行容器的名字, 默认为mysql
EOF
    exit 0
}

get_path() {
    if test -e "$1"; then
        realpath $1
    elif test "x${1:0:1}" = 'x/'; then
        echo $1
    else
        echo $(realpath .)/${1#*./}
    fi
}

parse_args() {
    GETOPT_ARGS=$(getopt -o $short_opt -al $long_opt -- "$@")
    eval set -- "$GETOPT_ARGS"

    while test -n "$1" ; do
        case "$1" in
            -u|--user) opt[u]=$2; shift 2;;
            -p|--password) opt[p]=$2; shift 2;;
            -P|--port) opt[P]=$2; shift 2;;
            -h|--host) opt[h]=$2; shift 2;;
            -w|--workdir) opt[w]=$(get_path $2); shift 2;;
            -d|--datadir) opt[d]=$(get_path $2); shift 2;;
            -c|--config) opt[c]=$(get_path $2); shift 2;;
            -n|--name) opt[n]=$2; shift 2;;
            -v|--version) opt[v]=$2; shift 2;;
            -s|--slave) opt[s]=$2; shift 2;;
            -t|--table) opt[t]=1; shift 1;;
            --) shift 1; break ;;
            *) break ;;
        esac
    done
    args="$@"
}

scriptfile=$0
cmd="$1"
grep -qw -e "$cmd" <<< "srv|master|slave|help|exec|restart" && shift
parse_args "$@"

# echo "cmd = $cmd, args = $args, opt = ${opt[*]}, scriptfile = $scriptfile"

case "$cmd" in
    srv)
        run_srv
        ;;
    master)
        run_master $args
        ;;
    slave)
        run_slave $args
        ;;
    exec)
        run_client exec $args
        ;;
    restart)
        docker restart ${opt[n]}
        ;;
    rm)
        docker stop ${opt[n]}
        docker rm ${opt[n]}
        ;;
    help)
        help_show
        ;;
    *)
        run_client run $args
        ;;
esac

